me = localPlayer

-- Primary functionality
local function runString (commandstring)
	outputChatBoxR("Executing client-side command: "..commandstring)
	local notReturned
	--First we test with return
	local commandFunction,errorMsg = loadstring("return "..commandstring)
	if errorMsg then
		--It failed.  Lets try without "return"
		commandFunction, errorMsg = loadstring(commandstring)
	end
	if errorMsg then
		--It still failed.  Print the error message and stop the function
		outputChatBoxR("Error: "..errorMsg)
		return
	end
	--Finally, lets execute our function
	local results = { pcall(commandFunction) }
	if not results[1] then
		--It failed.
		outputChatBoxR("Error: "..results[2])
		return
	end

	local resultsString = ""
	local first = true
	for i = 2, #results do
		if first then
			first = false
		else
			resultsString = resultsString..", "
		end
		local resultType = type(results[i])
		if isElement(results[i]) then
			resultType = "element:"..getElementType(results[i])
		end
		resultsString = resultsString..tostring(results[i]).." ["..resultType.."]"
	end

	if #results > 1 then
		outputChatBoxR("Command results: " ..resultsString)
		return
	end

	outputChatBoxR("Command executed!")
end

addEvent("doCrun", true)
addEventHandler("doCrun", root, runString)

-- Variable watching feature
local watchFuncs = {}
local watchKeys = {}
local watchFunc_G = function(k)
	return _G[k]
end

local function watchRender()
	local sw, y = guiGetScreenSize()
	local strings = {}
	local maxWidth = 0

	-- Offset bottom by some more
	y = y - 20

	for _, key in ipairs(watchKeys) do
		local results = { pcall(watchFuncs[key], key) }
		for i, v in ipairs(results) do
			results[i] = tostring(v)
		end

		local text = table.concat(results, ", ")
		table.insert(strings, key .. ": " .. text)

		local textWidth = dxGetTextWidth(text)
		if textWidth > maxWidth then
			maxWidth = textWidth
		end
	end

	for _, text in ipairs(strings) do
		y = y - 20
		dxDrawText(text, sw - 100 - maxWidth, y)
	end
end

local function watch(key, v)
	-- Error checking
	if type(v) ~= "string" and type(v) ~= "function" and type(v) ~= "nil" then
		error("Unexpected watch value of type " .. type(v) .. " for key " .. key)
		return
	end

	-- Remove if missing value
	if not v then
		-- Don't do anything if key doesn't exist
		if not watchFuncs[key] then
			return false
		end

		watchFuncs[key] = nil

		table.remove(watchKeys, table.find(watchKeys, key))
		table.sort(watchKeys)

		-- Remove event handler if we have no keys left
		if #watchKeys == 0 then
			removeEventHandler("onClientRender", root, watchRender)
		end

		return true
	end

	-- Only insert key if it doesn't already exist
	if not watchFuncs[key] then
		table.insert(watchKeys, key)
		table.sort(watchKeys)

		-- Add event handler if this is our first key
		if #watchKeys == 1 then
			addEventHandler("onClientRender", root, watchRender)
		end
	end

	-- Set watchFunc as global watcher, if v is string
	if type(v) == "string" then
		watchFuncs[key] = watchFunc_G
	elseif type(v) == "function" then
		watchFuncs[key] = v
	else
		assert(false, "never reached")
	end

	return true
end

runcode = {
	me = localPlayer,
	watch = watch
}
